package com.chenjt.netty.eight_nio.selector;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * 注释：一个线程监听多个端口，N多个客户端连接不同端口，处理N多个客户端的连接
 * 开了5个端口，用一个线程去监听5个端口上的事件，当相应的事件发生之后，对应的事件就会执行
 *
 * @author chenjt
 * @date 2024/8/23 9:39
 */
public class OneServerMultipleClient {

    public static void main(String[] args) throws Exception {

        int[] ports = new int[5];

        ports[0] = 5000;
        ports[1] = 5001;
        ports[2] = 5002;
        ports[3] = 5003;
        ports[4] = 5004;

        Selector selector = Selector.open();

        for (int i = 0; i < ports.length; ++i) {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);// 非阻塞
            ServerSocket serverSocket = serverSocketChannel.socket();
            InetSocketAddress address = new InetSocketAddress(ports[i]);
            serverSocket.bind(address);

            // 将serverSocketChannel注册到selector，serverSocketChannel关注OP_ACCEPT事件（接受连接事件），一个selector可以被多个serverSocketChannel注册
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println("监听端口：" + ports[i]);
        }


        while(true){
            // 会一直阻塞在这里，返回所关注的事件的数量
            int numbers = selector.select();
            System.out.println("numbers:" + numbers);

            // 所关注事件的集合（serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);所以这里serverSocketChannel关注的事件是接受连接事件OP_ACCEPT）
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            System.out.println("selectedKeys:" + selectionKeys);

            Iterator<SelectionKey> iter = selectionKeys.iterator();

            while(iter.hasNext()){
                SelectionKey selectionKey = iter.next();

                if(selectionKey.isAcceptable()){// 服务器端已经接受了客户端的连接
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
                    SocketChannel socketChannel = serverSocketChannel.accept();// 获取到客户端和服务器端连接的SocketChannel
                    socketChannel.configureBlocking(false);// 非阻塞

                    // 将socketChannel注册到selector，socketChannel关注OP_READ事件（数据读取事件）
                    socketChannel.register(selector,SelectionKey.OP_READ);

                    iter.remove();// 这里一定要清空，否则下一次循环进来时，这个SelectionKey已经不存在了，会导致出现空指针异常

                    System.out.println("获得客户端连接：" + socketChannel);

                }else if(selectionKey.isReadable()){// 有数据可读
                    SocketChannel socketChannel = (SocketChannel)selectionKey.channel();

                    int bytesRead = 0;

                    while(true){
                        ByteBuffer byteBuffer = ByteBuffer.allocate(512);

                        byteBuffer.clear();

                        int read = socketChannel.read(byteBuffer);

                        if(read <= 0){
                            break;
                        }

                        byteBuffer.flip();

                        socketChannel.write(byteBuffer);

                        bytesRead += read;

                    }

                    System.out.println("读取：" + bytesRead + "，来自于：" + socketChannel);
                    iter.remove();// 这里一定要清空，否则下一次循环进来时，这个SelectionKey已经不存在了，会导致出现空指针异常

                }
            }
        }
    }

}
